package Spider;

##########################
#     OmnibusProxy       #
#    Krzysztof Ulman     #
# Politechnika Krakowska #
#      Kraków 2012       #
##########################

use strict;
use warnings;
use encoding 'utf8';
use utf8;

use LWP::ConnCache;
use WWW::Mechanize;

use AppConfig;
use Screen;
use SpiderController;

#obiekt wyszukiwarki, ktorej dotyczy ten pajak
#my $searcherDto;

#zarzadzanie intervalem
#my $spiderController;

#obiekt WWW:Mechanize do pobierania danych
#my $browser;

#obiekt WWW:Mechanize trzymajacy pobrana strone glowna wyszukiwarki
#my $baseBrowser;

#dostepne wrtosci dla naglownka User-Agent
our %userAgents = (
				'IE_8'		=> 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0; pl)',
				'IE_9'		=> 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)',
				'FIREFOX_3'	=> 'Mozilla/5.0 (Windows; U; Windows NT 6.1; pl; rv:1.9.2.8) Gecko/20100722 Firefox/3.6.8',
				'FIREFOX_11'=> 'Mozilla/5.0 (Windows NT 6.1; rv:11.0) Gecko/20100101 Firefox/11.0',
				'CHROME_7'	=> 'Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.7 (KHTML, like Gecko) Chrome/7.0.517.44 Safari/534.7',
				'CHROME_18'	=> 'Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.7 (KHTML, like Gecko) Chrome/18.0.1025.151 Safari/534.7',
				'OPERA_10'	=> 'Opera/9.80 (Windows NT 6.1; U; pl) Presto/2.6.30 Version/10.63',
				'OPERA_11'	=> 'Opera/9.80 (Windows NT 6.1; U; pl) Presto/2.10.299 Version/11.62'
				);

our %headerLanguage = (
				'IE_8'		=> 'pl-PL,pl;q=0.9,en;q=0.8',
				'IE_9'		=> 'pl-PL,pl;q=0.9,en;q=0.8',
				'FIREFOX_3'	=> 'pl,en-us;q=0.7,en;q=0.3',
				'FIREFOX_11'=> 'pl,en-us;q=0.7,en;q=0.3',
				'CHROME_7'	=> 'pl-PL,pl;q=0.8,en-US;q=0.6,en;q=0.4',
				'CHROME_18'	=> 'pl-PL,pl;q=0.8,en-US;q=0.6,en;q=0.4',
				'OPERA_10'	=> 'pl-PL,pl;q=0.9,en;q=0.8',
				'OPERA_11'	=> 'pl-PL,pl;q=0.9,en;q=0.8'
				);
				
our %headerAccept = (
				'IE_8'		=> 'text/html, application/xml;q=0.9, application/xhtml+xml, image/png, image/webp, image/jpeg, image/gif, image/x-xbitmap, */*;q=0.1',
				'IE_9'		=> 'text/html, application/xml;q=0.9, application/xhtml+xml, image/png, image/webp, image/jpeg, image/gif, image/x-xbitmap, */*;q=0.1',
				'FIREFOX_3'	=> 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
				'FIREFOX_11'=> 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
				'CHROME_7'	=> 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
				'CHROME_18'	=> 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
				'OPERA_10'	=> 'text/html, application/xml;q=0.9, application/xhtml+xml, image/png, image/webp, image/jpeg, image/gif, image/x-xbitmap, */*;q=0.1',
				'OPERA_11'	=> 'text/html, application/xml;q=0.9, application/xhtml+xml, image/png, image/webp, image/jpeg, image/gif, image/x-xbitmap, */*;q=0.1'
				);

#FIREFOX
#Accept	text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
#Accept-Encoding	gzip, deflate
#Accept-Language	pl,en-us;q=0.7,en;q=0.3
#Cache-Control	max-age=0
#Connection	keep-alive
#Host	www.google.pl
#User-Agent	Mozilla/5.0 (Windows NT 6.1; rv:11.0) Gecko/20100101 Firefox/11.0

#OPERA
#GET /search?q=testowe+zapytanie&sourceid=opera&num=0&ie=utf-8&oe=utf-8 HTTP/1.1 
#User-Agent: Opera/9.80 (Windows NT 6.1; U; pl) Presto/2.10.229 Version/11.62
#Host: www.google.com
#Accept: text/html, application/xml;q=0.9, application/xhtml+xml, image/png, image/webp, image/jpeg, image/gif, image/x-xbitmap, */*;q=0.1
#Accept-Language: pl-PL,pl;q=0.9,en;q=0.8
#Accept-Encoding: gzip, deflate
#Connection: Keep-Alive

#IE
#GET /search?q=testowe+zapytanie&sourceid=opera&num=0&ie=utf-8&oe=utf-8 HTTP/1.1 
#User-Agent: Opera/9.80 (Windows NT 6.1; U; pl) Presto/2.10.229 Version/11.62
#Host: www.google.com
#Accept: text/html, application/xml;q=0.9, application/xhtml+xml, image/png, image/webp, image/jpeg, image/gif, image/x-xbitmap, */*;q=0.1
#Accept-Language: pl-PL,pl;q=0.9,en;q=0.8
#Accept-Encoding: gzip, deflate
#Connection: Keep-Alive

#CHROME
#GET /search?sourceid=chrome&ie=UTF-8&q=busy+Krak%C3%B3w HTTP/1.1\r\n
#Host: www.google.pl\r\n
#User-Agent: Mozilla/5.0 (Windows NT 6.1) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.162 Safari/535.19\r\n
#Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n
#Accept-Encoding: gzip,deflate,sdch\r\n
#Accept-Language: pl-PL,pl;q=0.8,en-US;q=0.6,en;q=0.4\r\n
#Accept-Charset: ISO-8859-2,utf-8;q=0.7,*;q=0.3\r\n

#konstruktor
#klasa odpowiedzialna za pobieranie danych z jednej, zdefiniowanej wyszukiwarki 
sub new {
	shift;
	my $self = {};
	bless $self;
	return $self;
}

#konfiguruje robota do korzystania z wybranej wyszukiwarki
sub prepare {
	my $self = shift;

	#setowanie obiektu wyszukiwarki
	my $searcherDto = shift;
	$self->{searcherDto} = $searcherDto;
	Screen->printAndLogDebug('Przygotowanie spidera dla ' . $searcherDto->getName());
	
	#kontroler
	my $spiderController = SpiderController->new($searcherDto->getSearcherId());
	$self->{spiderController} = $spiderController;
	
	#przygotowanie wyszukiwarki
	my $browser = private_createBrowser();
	
	#pobranie strony glownej wyszukiwarki
	#pobranie to jest juz liczone w SpiderController
	$browser->get($searcherDto->getUrl());
	if($browser->success()) {
		$self->{baseBrowser} = $browser;
		my $clonedBrowser = private_cloneBrowser($browser);
		$self->{browser} = $clonedBrowser;
	} else {
		die "Nie udalo się pobrac strony " . $searcherDto->getUrl() . ", blad: " . $browser->response()->status_line;
	}
}

#tworzy i konfiguruje nowy obiekt przegladarki
sub private_createBrowser {
	my $browser = WWW::Mechanize->new();

	#podszywanie sie pod przegladarke
	$browser = private_setUserAgent($browser); 
	
	#tablica na ew. ciasteczka
	$browser->cookie_jar({});
	
	#obsluga keep-alive
	$browser->conn_cache(LWP::ConnCache->new());
	
	#wylaczenie historii
	$browser->stack_depth(0);
	
	#wylaczenie wysylania naglowka referer
	#$browser->add_header( Referer => undef );
	
	#czas oczekiwania na odpowiedz
	$browser->timeout(AppConfig->getProperty('SPIDER_TIMEOUT'));
	
	#dodatkowe naglowki
	$browser->add_header('Accept-Encoding' => 'gzip, deflate');
	
	return $browser;
}

#zamienia obiekt przegladarki na inny
sub private_replaceBrowser {
	my $self = shift;
	my $baseBrowser = $self->{baseBrowser};
	
	#kolnujemy podstawowa przegladarke, aby miec dostep do strony glownej i rzeczywistego base_url
	my $newBrowser = private_cloneBrowser($baseBrowser);
	
	#podszywanie sie teraz pod inna przegladarke 
	$newBrowser = private_setUserAgent($newBrowser);
	
#	#zwracamy "nowiutka" przegladarke
#	return $newBrowser;
	$self->{browser} = $newBrowser;
}

#tworzy kopie obiektu przegladarki i odpowiednio setuje niekopiowalne wartosci
sub private_cloneBrowser {
	my $browser = shift;
	#wlasciwe klonowanie
	my $clone = $browser->clone();
	#czyscimy ciasteczka
	$clone->cookie_jar({});
	#ustawiamy keep-alive - aby nawiazalo zupelnie nowe polaczenie i je potem trzymalo
	$clone->conn_cache(LWP::ConnCache->new());
	return $clone;
}

#ustawia losowa przegladarke
sub private_setUserAgent {
	my $browser = shift;
	
	#losujemy agenta
	my @agentsNames = keys(%userAgents);
	my $agentName = $agentsNames[int(rand(scalar(@agentsNames)))];
	my $agent = $userAgents{$agentName};

	#setujemy
	$browser->agent($agent);
	
	#dodatkowe naglowki zalezne od przegladarki
	$browser->add_header('Accept' => $headerAccept{$agentName});
	$browser->add_header('Accept-Language' => $headerLanguage{$agentName});
	
	#zwracamy
	Screen->printAndLogDebug('Podszywam sie pod: ' . $agentName);
	return $browser; 
}

#pobranie odpowiedzi na dana fraze
sub doQuery {
	my $self = shift;
	my $phase = shift;
	my $spiderController = $self->{spiderController};
	
	if($spiderController->needBrowserReplacement()) {
		#utworzenie nowej przegladarki
		private_replaceBrowser($self);
	}
	#pobranie obiektu przegladraki
	my $browser = $self->{browser};

	#zrobienie przerwy
	$spiderController->doInterval();
	
	#wybranie pierwszego formularza na stronie
	$browser->form_number(1);
	#znalezienie wszystkich inputow typu "text"
	my @textInputs = $browser->find_all_inputs(type => 'text');
	if(scalar(@textInputs) != 1) {
		Screen->printAndLogError('Liczba elementow typu input: ' . scalar(@textInputs));
		Screen->printAndLogDebug('\n================\n==============\n' . $browser->content() . "\n================\n");
		#nie do konca jasna sytuacja, dlaczdgo tutaj czasami wpada pod dluzszym czasie pracy
		#ale sprobujmy stworzyc obiekt przegladarki na podstawie wzorcowego
		my $searcherDto = $self->{searcherDto};
		Screen->printAndLogInfo('Probuje stworzyc obiekt przegladarki dla wyszuiwarki ' . $searcherDto->getName() . ' na podstawie wzorca');
		private_replaceBrowser($self);
		#pobranie utworzonego obiegtu przegladarki
		$browser = $self->{browser};
		#ponowny wybor formularza
		$browser->form_number(1);
		#i znalezienie wszystkich inputow typu "text"
		@textInputs = $browser->find_all_inputs(type => 'text');
		if(scalar(@textInputs) != 1) {
			#to ja juz nie wiem co robic, logujemy i wywalamy aplikacje
			Screen->printAndLogError('Liczba elementow typu input: ' . scalar(@textInputs));
			Screen->printAndLogDebug('\n================\n==============\n' . $browser->content() . "\n================\n");
			die 'Nie znaleziono dokladnie jednego elementu input typu text do wpisania frazy.';
		}
	}
	my $input = $textInputs[0];
	#wstawienie frazy do inputa
	$browser->field( $input->name => $phase);
	#wyslanie zapytania
	$browser->submit();
	if($browser->success()) {
		return $browser->content(); #zwrocenie kodu html
	} else {
		die "Nie udało się otrzymać odpowiedzi na zapytanie o: {" . $phase . "} ";
	}
	
	return 0;
}

#pobranie konkretnego adresu URL (kolejne strony)
sub doGet {
	my $self = shift;
	my $url = shift;
	
	#utworzenie kopii strony glownej wyszukiwarki
	my $browser = $self->{browser};
	
	#zrobienie przerwy
	my $spiderController = $self->{spiderController};
	$spiderController->doInterval();
	
	#wyslanie zapytania
	$browser->get($url);
	if($browser->success()) {
		return $browser->content(); #zwrocenie kodu html
	} else {
		die "Nie udalo sie pobrac: $url";
	}
	
	return 0;
}

#zwraca liczbe stron pobranych przez ten obiekt
sub getTotalPageCounter {
	my $self = shift;
	return $self->{spiderController}->getPageCounter();
}

#zwraca DTO wyszukiwarki
sub getSearcherDto {
	my $self = shift;
	return $self->{searcherDto};
}

1;